test(plugin-calendar,plugin-gantt): the colorField ladder fixtures name their own writer (objectui#7828) - #7892
Merged
Conversation
…me their own writer (objectui#7828) The gantt and calendar `colorField` ladder fixtures were still in the pre-objectui#7521 harness shape that produced objectui#7466's intermittent red: a module-level mutable the mock writes, an arity-only readiness predicate (`expect(lastTasks.length).toBe(rows.length)`), no unmount, and a return value re-read from the global after the predicate accepted a different one. objectui#7527 (`c2fc261f5`) hardened the timeline fixture and touched that package only; PR #7826 added the bound capture there. This ports both to the two siblings. Four changes, none of them about the ladder itself: 1. the readiness predicate identifies WHICH render wrote — a title token the call owns, riding through `titleField` — instead of counting arity, which cannot separate "the component I mounted has painted" from "an earlier one painted again"; 2. the value returned is the array that predicate accepted, not a second read of the module global taken after `act` has yielded; 3. every render is torn down in a `finally`; 4. no renderer may be alive when one is mounted. In the gantt file the two `it` blocks that mounted inline now go through the same helper, because guard 4 is only worth anything if every render in the file is accounted for. Measured rather than assumed, because a green loop is not evidence here -- objectui#7466 paid for that reading, where the broken timeline harness passed 32/32 on an idle box, the same 32/32 the fixed one gets. The lever is the metadata fetch, so the lever was swept: the pre-port harness shape driving two renders inside one `it`, with `getObjectSchema` held by 0/1/2/3/4/5/6/7/8/9/10/12/15/20/25/35/50 ms, three runs per hold, all three components in the same vitest runs. ObjectTimeline (ungated control) 20 fail / 60 runs ObjectCalendar 0 fail / 60 runs ObjectGantt 0 fail / 60 runs Every timeline failure sits at a hold of 1-9ms, with the card's exact signature. The two zeroes are readable only because that control fired in the same runs. So neither sibling reproduces the failure, and this does not claim the timeline's two-paint behaviour for them: both GATE their record query on the settled object schema (objectui#6453 / objectui#7225), so their first paint arrives after the metadata read -- the measured paint time tracks the hold -- and no schema-triggered second paint follows. What is ported is the harness half, which does not depend on that: a module global plus an arity-only predicate cannot say which component wrote, whatever the component does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
This was referenced Sep 6, 2026
os-sam
marked this pull request as ready for review
September 6, 2026 02:06
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7828
Ports objectui#7527's three harness guards (
c2fc261f5) plus PR #7826's bound capture to the two siblingcolorFieldladder fixtures. Test fixtures only — no runtime code, no gate predicate, no gate scan population.packages/core/src/utils/record-color.tsis untouched: #7466 cleared the shared ladder of any role.Why touch a file that is green
The exposure lever is two renders inside one
it. RTL's auto-cleanup runs inafterEach, so acrossitblocks the previous writer is already unmounted; only inside oneitdo two components stay alive against one module global. The card checked mechanically that noitin either file renders twice today — which is why nothing is red.So these fixtures are not failing. They are one edit away from failing, and that edit is "add a second assertion to one of those
itblocks" — the ordinary way to extend a ladder fixture, what the timeline file's rung 2 already does, and precisely the edit that produced #7466. The guards are what make that next edit safe.What changed
Four points, both files:
titleField— instead of counting arity.lastEvents.length === rows.lengthcannot separate "the component I just mounted has painted" from "an earlier one painted again";acthas yielded on its way out ofwaitFor;finally;In the gantt file the two
itblocks that mounted inline now go through the same helper. Guard 4 is only worth anything if every render in the file is accounted for — an inline render left mounted is exactly what it is meant to catch.Evidence — outcome (b): the two siblings do NOT paint twice
Reported honestly, and stated plainly: no failure was reproducible on either file today.
A green loop is not evidence here and none is offered. #7466 paid for that reading — the broken timeline harness passed 32/32 on an idle box, the same 32/32 the fixed one gets. The lever is the metadata fetch, not machine load, so the lever was swept.
Instrument: the pre-port harness shape (module global, arity-only predicate, no unmount, re-read of the global) driving two renders inside one
it— the #7466 exposure shape — withgetObjectSchemaheld by 0/1/2/3/4/5/6/7/8/9/10/12/15/20/25/35/50 ms, three runs per hold, all three components in the same vitest runs:ObjectTimeline(ungated, positive control)ObjectCalendarObjectGanttThe control is what makes those two zeroes readable at all: the instrument does fire, in this container, on this day, in those same runs — with the card's exact signature, the second render reading the first render's colour back out of the global (
second=["#abc"]where["#123456"]was authored). Note where it fires: every timeline failure sits at a hold of 1-9ms, and the first, coarser grid caught it at one hold out of eight. That is why the grid was refined instead of repeated — repetition at a hold outside the window is the 32/32 that taught #7466 nothing.And the mechanism, measured the same way — writes into the module global after the readiness predicate went green, render left mounted exactly as the pre-port harness leaves it:
ObjectTimeline,getObjectSchema+25msObjectCalendar, +0 / +25 / +100msObjectGantt, +0 / +25 / +100msObjectTimeline's data effect listsobjectDef, so it paints once before the metadata lands and again after. Both siblings gate their record query on the settled object schema (objectui#6453 for the calendar, objectui#7225 ask 2 for the gantt, today the shareduseSettledSchema), so their first paint arrives only after the metadata read — the measured paint time tracks the hold, 34ms/104ms for the calendar and 31ms/114ms for the gantt at +25/+100 — and no schema-triggered second paint follows.So this PR does not claim the timeline's two-paint behaviour for either sibling; it was looked for, with a lit instrument, and it is not there. What is ported is the harness half, which does not depend on it — in the card's own words, a module global plus an arity-only predicate cannot say which component wrote, whatever the components do. Both file headers carry these numbers.
Changeset
Test-only, so the empty-frontmatter form is the expected pass.
node scripts/check-changeset-presence.mjs, verbatim:(The verdict line is prefixed with a green check in the real output; transcribed here as
OKso the character survives.)Verification
Re-run after the final commit, on the pushed SHA
2dc8ba4f8, withgit diff HEADempty:pnpm exec vitest run packages/plugin-calendar/ packages/plugin-gantt/— 87 files / 634 tests passedpnpm --filter @object-ui/plugin-calendar --filter @object-ui/plugin-gantt run type-check— Done for both (tsc --noEmit && tsc -p tsconfig.test.json;--listFilesconfirms both edited fixtures are in that program, so this is a real reading about them)pnpm --filter ... run lintfor both packages — 0 errors (161 and 353 pre-existing warnings)check-changeset-presence,check-changeset-no-major,check-changeset-overwrite,check-control-bytes,check-vi-mock-specifiers,check-vi-mock-inherit,check-unreferenced-sources,check-lint-coverage— all exit 0check-governed-queue-guard --teston all three changed paths —NOT GOVERNED, 3 path(s) checked against 5 governed surface(s); none matchedRepo-wide
pnpm lint(turbo, 46 packages) is left to CI; the two packages whose files changed were linted in full, and the config is not type-aware, so this change cannot move a judgement on a file it does not touch.Generated by Claude Code